﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

namespace Needle.Bus.Transport.Impl.WCF
{

    public class WCFChannel<T>: IChannel<T>
    {

        private interface IWCFChannel :  IChannel<T>, IClientChannel
        { }

        ITransport _Owner;
        ChannelFactory<IWCFChannel> factory;
        IWCFChannel client;

        public WCFChannel(string topic)
        {
            if (String.IsNullOrEmpty(topic))
                throw new ArgumentException("topic is null or empty.", "topic");
            _Topic = topic;
            factory = CreateChannelFactory();
        }

        private ChannelFactory<WCFChannel<T>.IWCFChannel> CreateChannelFactory()
        {
            ContractDescription contract = ContractDescription.GetContract(typeof(IChannel<T>));
            Binding binding = WCFSettings.Binding;
            EndpointAddress address = WCFSettings.Address<T>(_Topic);
            ServiceEndpoint serviceEndpoint = new ServiceEndpoint(contract, binding, address);
            serviceEndpoint.Name = _Topic;
            return new ChannelFactory<IWCFChannel>(serviceEndpoint);
        }

        #region IChannel<T> Members

        private string _Topic;
        public string Topic
        {
            get { return _Topic; }
        }

        public void Send(T message)
        {
            if (client == null || client.State != CommunicationState.Created || client.State != CommunicationState.Opened)
                client = factory.CreateChannel();
            client.Send(message);
        }

        #endregion

        #region IExtension<ITransport> Members

        void IExtension<ITransport>.Attach(ITransport owner)
        {
            if (_Owner != null)
                throw new InvalidOperationException("already attached to owner");
            _Owner = owner;
        }

        void IExtension<ITransport>.Detach(ITransport owner)
        {
            if (_Owner != owner)
                throw new InvalidOperationException("wrong owner");
            _Owner = null;
            try
            {
                if (client != null && client.State == CommunicationState.Opened)
                    client.Close();
            }
            catch (Exception)
            { /* eat exceptions */ }
            finally
            {
                client = null;
            }
            try
            {
                if (this.factory != null && factory.State == CommunicationState.Opened)
                    factory.Close();

            }
            catch (Exception)
            { /* eat exceptions */ }
        }

        #endregion
    }
}
